/*
Copyright Inspur Corp. 2022-2022 All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package updatebatch

import (
	"ichain/ledger/kvledger/statedb"
	"ichain/ledger/util"

	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"
)

type UpdatesBytesBuilder struct {
}

// 根据UpdateBatch获取proto类型的KVWritesBatchProto，用于计算提交哈希
func (bb *UpdatesBytesBuilder) DeterministicBytesForUpdates(batch *statedb.UpdateBatch) ([]byte, error) {
	namespaces := dedupAndSort(batch.GetUpdatedNamespaces())

	kvWritesProto := []*KVWriteProto{}
	for _, ns := range namespaces {
		if ns == "" {
			// an empty namespace is used for persisting the channel config
			// skipping the channel config from including into commit hash computation
			// as this proto uses maps and hence is non deterministic
			continue
		}
		p := bb.buildForKeys(batch.GetUpdates(ns))
		p[0].Namespace = ns
		kvWritesProto = append(kvWritesProto, p...)
	}
	batchProto := &KVWritesBatchProto{
		Kvwrites: kvWritesProto,
	}

	batchBytes, err := proto.Marshal(batchProto)
	return batchBytes, errors.Wrap(err, "error constructing deterministic bytes from update batch")
}

func (bb *UpdatesBytesBuilder) buildForKeys(kv map[string]*statedb.VersionedValue) []*KVWriteProto {
	keys := util.GetSortedKeys(kv)
	p := []*KVWriteProto{}
	for _, key := range keys {
		val := kv[key]
		p = append(
			p,
			&KVWriteProto{
				Key:          []byte(key),
				Value:        val.Value,
				IsDelete:     val.Value == nil,
				VersionBytes: val.Version.ToBytes(),
			},
		)
	}
	return p
}

func dedupAndSort(a []string) []string {
	m := map[string]struct{}{}
	for _, entry := range a {
		m[entry] = struct{}{}
	}
	return util.GetSortedKeys(m)
}
